﻿using System;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using SuperRyoninRPG.Characters.Enemys;
using SuperRyoninRPG.Controller;
using SuperRyoninRPG.Display;
using SuperRyoninRPG.Main;

namespace SuperRyoninRPG.Characters
{
	/// <summary>
	///     X 軸に沿った向き。
	/// </summary>
	public enum FaceDirection
	{
		Left = -1,
		Right = 1,
	}

	public abstract class Character:Thing
	{
		public enum BaseAction
		{
			Idle,
			Run,
			Brake,
			Jump,
			Die,
			Crouch
		}

		public enum BaseState
		{
			Normal
		}

		private readonly Animations animations;
		public override Animation Animation
		{
			get { return animations[this.state,this.action]; }
		}
		private readonly string imagesPath;
		private string state;

		/// <summary>
		///     現在のユーザーの動きの入力。
		/// </summary>
		private string action;

		private SoundEffect dieSound;

		private SoundEffect jumpSound;
		private SoundEffect killedSound;


		/// <summary>
		///     新しいプレイヤーを構築します。
		/// </summary>
		protected Character(Stage stage, Vector2 position, string imagesPath, int frameWidth) : base(stage,position,frameWidth)
		{
			this.imagesPath = imagesPath;
			this.state = BaseState.Normal.ToString();
			this.action = BaseAction.Idle.ToString();

			this.animations = new Animations();

			this.Initialize();
			this.LoadContent();

			this.Reset(position);
		}

		public string State
		{
			get { return this.state; }
			set
			{
				this.state = value;
				this.Sprite.PlayAnimation(this.animations[value, this.action]);
			}
		}

		public string Action
		{
			get { return this.action; }
			set
			{
				this.action = value;
				this.Sprite.PlayAnimation(this.animations[this.state, value]);
			}
		}

		


		protected abstract string[] GetStates();
		protected abstract string[] GetActions();

		private void Initialize()
		{
			this.AllowTurnImage = true;
		}

		/// <summary>
		///     プレイヤーのスプライト シートとサウンドを
		///     読み込みます。
		/// </summary>
		public void LoadContent()
		{
			// アニメーション表示された
			// テクスチャーを読み込みます。
			this.LoadAnimations();

			// サウンドを
			// 読み込みます。
			this.killedSound = this.Stage.Content.Load<SoundEffect>("Sounds/PlayerKilled");
			this.jumpSound = this.Stage.Content.Load<SoundEffect>("Sounds/PlayerJump");
			this.dieSound = this.Stage.Content.Load<SoundEffect>("Sounds/PlayerKilled");
		}

		public void LoadAnimations()
		{
			foreach (string characterState in Enum.GetNames(typeof (BaseState)))
			{
				this.LoadActionAnimations(characterState);
			}
			foreach (string characterState in this.GetStates())
			{
				this.LoadActionAnimations(characterState);
			}
		}

		public void LoadActionAnimations(string characterState)
		{
			foreach (string characterAction in Enum.GetNames(typeof (BaseAction)))
			{
				this.LoadAnimation(characterState, characterAction);
			}

			foreach (string characterAction in this.GetActions())
			{
				this.LoadAnimation(characterState, characterAction);
			}
		}

		public void LoadAnimation(string characterState, string characterAction)
		{
			string path = Path.Combine(this.imagesPath, characterState, characterAction);
			var texture = this.Stage.Content.Load<Texture2D>(path);
			var newAnimations = new Animation(texture, this.FrameWidth, 0.1f, true);

			this.animations.Add(characterState, characterAction, newAnimations);
		}

		/// <summary>
		///     プレイヤーをリセットして
		///     復活させます。
		/// </summary>
		/// <param name="position">復活する位置。</param>
		public void Reset(Vector2 position)
		{
			this.Position = position;
			this.Velocity = Vector2.Zero;
			this.IsAlive = true;
			this.Action = BaseAction.Idle.ToString();
		}

		/// <summary>
		///     入力を処理し、物理処理を実行して、プレイヤー スプライトを
		/// </summary>
		/// <remarks>
		///     すべての入力ステートが 1 フレームごとにポーリングされるよう該当するステート変数を渡します。
		///     加速度センサーの計測値をゲームに正しく反映するためにはゲームの向きである DisplayOrientation も渡す必要があります。
		///     具体的には向きが LandscapeRight に設定されている場合は加速度センサーの値を反転する必要があります。
		/// </remarks>
		protected override void OnUpdate(GameTime gameTime)
		{
			base.OnUpdate(gameTime);
			if (this.IsAlive && this.IsOnGround)
			{
				if (Math.Abs(this.Velocity.X) > 0 || Math.Abs(this.Movement) > 0)
				{
					if (this.IsCrouching)
					{
						this.Action = BaseAction.Crouch.ToString();
					}
					if ((this.MoveAcceleration > 0 && this.Movement < 0)
					    || (this.MoveAcceleration < 0 && this.Movement > 0)
					    || (this.MoveAcceleration == 0 && this.Action == BaseAction.Brake.ToString()))
					{
						this.Action = BaseAction.Brake.ToString();
					}
					else
					{
						this.Action = BaseAction.Run.ToString();

						this.animations[this.state, BaseAction.Run.ToString()].FrameTime = this.IsDushing
							? 0.05f
							: 0.1f;
					}
				}
				else
				{
					this.Action = BaseAction.Idle.ToString();
				}
			}

			// 入力を作成します。
			this.Movement = 0.0f;
			this.IsJumping = false;
		}

		protected override void OnStartJump()
		{
			base.OnStartJump();
			if (this.IsAlive)
			{
				this.Action = BaseAction.Jump.ToString();
				
			}
		}

		/// <summary>
		///     プレイヤーが倒されたときに呼び出されます。
		/// </summary>
		/// <param name="killedBy">
		///     プレイヤーを倒した敵。プレイヤーが敵に倒されなかった場合 (穴に落ちた場合)、
		///     このパラメーターは null です。
		/// </param>
		public override void OnDamaged(Character killedBy)
		{
			base.OnDamaged(killedBy);
		}

		public override void OnKilled(Character killedBy)
		{
			base.OnKilled(killedBy);

			this.IsAlive = false;

			if (killedBy != null)
			{
				this.dieSound.Play();
			}
			this.Action = BaseAction.Die.ToString();
		}
	}
}